package com.spring.nio.core.configuration;

import com.spring.nio.core.event.context.EventHandlerContext;
import com.spring.nio.core.event.handler.AbstractEventHandler;
import com.spring.nio.core.event.listener.ExceptionListener;
import com.spring.nio.core.event.listener.FileHandlerListener;
import com.spring.nio.core.event.listener.manage.ApplicationEventDispatchManager;
import com.spring.nio.core.reactor.selector.SelectorTypeEnum;
import com.spring.nio.util.LocalTime;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.channels.Selector;
import java.time.LocalDateTime;
import java.util.*;

/**
 * @author: YSFY
 * @date: 2021-03-24 19:00
 * @pacage_name: com.spring.nio.core.configuration
 * @class_name: NioServerBootstrap
 * @description: Nio 服务引导类，面向接口编程
 *
 *  创建和启动Selector、acceptor，提供自定义增强socketChannel
 */

@Slf4j
@SuppressWarnings("all")
public final class NioServerBootstrap {
    private static NioServerBootstrap nioServerBootstrap = new NioServerBootstrap();
    private static BootstrapConfig bootstrapConfig = new BootstrapConfig();

    private NioServerBootstrap() {}

    public static NioServerBootstrap getBootstrap() {
        return nioServerBootstrap;
    }

    public static BootstrapConfig getConfig() {
        return bootstrapConfig;
    }

    /**
     * 1、配置服务端Selector线程
     * */
    public NioServerBootstrap configSelector(String selectorName, Runnable runnable) throws IOException {
        SelectorEntity selectorEntity = SelectorEntity.getInstance()
            .setSelectorName(SelectorTypeEnum.CHAT_SELECTOR.getName())
            .setSelector(Selector.open())
            .setSelectorRunnable(runnable);
        bootstrapConfig.getSelectors().put(selectorName, selectorEntity);
        return this;
    }

    /**
     * 2、配置服务端Acceptor线程
     * @param acceptorName 名称
     * @param runnable acceptor线程
     * @param inetSocketAddress acceptor线程监听地址
     * */
    public NioServerBootstrap configAcceptor(String acceptorName, Runnable runnable, InetSocketAddress inetSocketAddress) throws UnknownHostException {
        AcceptorEntity acceptorEntity = AcceptorEntity.getInstance()
            .setAcceptorName(acceptorName)
            .setInetSocketAddress(inetSocketAddress)
            .setAcceptorRunnable(runnable);
        bootstrapConfig.getAcceptors().put(acceptorName, acceptorEntity);
        return this;
    }

    /**
     * 3、配置事件处理
     */
    public NioServerBootstrap configEventHandler(AbstractEventHandler abstractEventHandler) {
        EventHandlerContext.getEventHandlerContext().addEventHandler(abstractEventHandler);
        return this;
    }

    /**
     * 4、配置spring支持
     * */
    public NioServerBootstrap configSpringIOC() {
        if(Objects.isNull(bootstrapConfig.getClassPathXmlApplicationContext())) {
            bootstrapConfig.setClassPathXmlApplicationContext(new ClassPathXmlApplicationContext("spring/applicationContext.xml"));
        }

        log.info("[ " + LocalTime.formatDate(LocalDateTime.now()) + " ] NioServerBootstrap | --> spring container startup success, thread = [{}]", Thread.currentThread().getName());
        return this;
    }

    /**
     * 5、配置spring事件监听支持
     * */
    public NioServerBootstrap configEventListener() {
        ApplicationEventDispatchManager.configApplicationEventListener();
        return this;
    }

    /**
     * 6、配置心跳检测线程
     * */
    public NioServerBootstrap configHeatBeatThread() {
        bootstrapConfig.setHeartBeanThread(new Thread(new Runnable() {
            @Override
            public void run() {
                // 心跳线程每隔 一定时间 进行判断，是否接收到来自客户端的心跳请求，如果接收到则是活跃连接，否则将终止通道
            }
        }, "HEART_BEAT_THREAD"));
        bootstrapConfig.getHeartBeanThread().setDaemon(Boolean.TRUE);
        bootstrapConfig.getHeartBeanThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                log.info("[ " + LocalTime.formatDate(LocalDateTime.now()) + " ] ExceptionListener | --> 心跳检测线程信息 [{}] ", e.getMessage());
            }
        });

        return this;
    }

    /**
     * 7、服务端启动
     * */
    public void start() {
        // 1、启动Selector线程
        Map<String, SelectorEntity> selectorMap = bootstrapConfig.getSelectors();
        if(!selectorMap.isEmpty()) {
            selectorMap.values().stream().forEach(selectorEntity -> {
                Thread thread = new Thread(this.enhanceParam(selectorEntity.getSelectorRunnable(), selectorEntity, null), selectorEntity.getSelectorName() + "-THREAD");
                selectorEntity.setThread(thread);
                thread.start();
            });
        }

        // 2、启动Acceptor线程
        Map<String, AcceptorEntity> acceptorMap = bootstrapConfig.getAcceptors();
        if(!acceptorMap.isEmpty()) {
            acceptorMap.values().stream().forEach(acceptorEntity -> {
                Thread thread = new Thread(this.enhanceParam(acceptorEntity.getAcceptorRunnable(), null, acceptorEntity), acceptorEntity.getAcceptorName() + "-THREAD");
                acceptorEntity.setThread(thread);
                thread.start();
            });
        }

        // 3、启动心跳检测线程
        bootstrapConfig.getHeartBeanThread().start();
    }

    private Runnable enhanceParam(Runnable runnable, SelectorEntity selectorEntity, AcceptorEntity acceptorEntity) {
        if(null != selectorEntity) {
            Arrays.stream(runnable.getClass().getDeclaredMethods()).filter(method -> StringUtils.equals(method.getName(), "setSelectorEntity")).forEach(method -> {
                method.setAccessible(Boolean.TRUE);
                try {
                    method.invoke(runnable, selectorEntity);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            });
            return runnable;
        }

        if(null != acceptorEntity) {
            Arrays.stream(runnable.getClass().getDeclaredMethods()).filter(method -> StringUtils.equals(method.getName(), "setAcceptorEntity")).forEach(method -> {
                method.setAccessible(Boolean.TRUE);
                try {
                    method.invoke(runnable, acceptorEntity);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            });
            return runnable;
        }

        return null;
    }

    //

    //

    //
}
